﻿/*
 * This class implements the conversions between unicode/mbcs and char[]
 */
 
module dwt.internal.converter;

extern (Windows) 
{
	uint SysStringByteLen(/*BSTR*/ wchar*  bstr);
	void SysFreeString(/*BSTR*/ wchar* bstr);	
}


final class Converter
{
	private import Utf	= tango.text.convert.Utf;
	private import Int	= tango.text.convert.Integer;
	private import Txt	= tango.text.Util ;
	private import tango.sys.Common;
	private import tango.stdc.stringz;
	alias wchar TCHAR ;
	
	
	private import dwt.internal.win32.os;

	version(OLE_COM)
	{
		// private import dwt.internal.ole.win32.com;
	}

	
	// convert UTF-8 to MBCS
	alias StrToMBCS StrToMBCSs;
	static char[] StrToMBCS(char[] sc, uint codepage = 0)
	{
		char[] ret = sc;
		try{
			foreach (char c; sc){
				if (c >= 0x80)
				{ 
					char[] result;
				    int i;
				    wchar[] ws = Utf.toString16(sc);
				    result.length = OS.WideCharToMultiByte(codepage, 0, ws.ptr, ws.length, null, 0, null, null);
				    i = OS.WideCharToMultiByte(codepage, 0, ws.ptr, ws.length, result.ptr, result.length, null, null);
				    assert(i == result.length);
				    return result;
				}
		    }
	    }catch(Exception e){
			// do nothing
			ret = "";
		}
	    return ret;
	}
	
	// convert UTF-8 to MBCSz
	static char* StrToMBCSz(char[] sc)
	{	
		char* ret = null;
		try{
			ret =  toStringz( StrToMBCS(sc) );
		}catch(Exception e){
			// do nothing
			ret = "";
		}
		return ret;
	}
	

	static wchar[] StrToWCHARs(char[] sc){
		return Utf.toString16(sc);
	}

	static wchar* StrToWCHARz(char[] sc)
	{
		wchar[] ws;
		if(sc.length == 0){
			ws ~= '\0';
			return ws.ptr;
		}
		
		try{
			ws = Utf.toString16(sc);
			if(ws[--$] != '\0')
				ws ~= '\0';
		}catch(Object e){
			// do nothing
			// <Shawn Liu> Win32 API raise Access violate with null pointer, so make it not null but empty
			ws = "";
		}
		return ws.ptr;
	}
	
	static char[] MBCSsToStr(char[] string, uint codepage = 0){
		return MBCSzToStr(string.ptr , string.length, codepage);
	}
	
	static char[] MBCSzToStr(char* pString, int _length = -1, uint codepage = 0)
	{
		// null terminated string pointer
		if(_length == -1){
			_length = 0;
			while(*(pString + _length))
				++_length; 
		}
		// convert MBCS to UTF-8
		if(_length == 0)
			return null;
			
		wchar[] wcs = _mbcszToWs(pString, _length, codepage);
		return Utf.toString( wcs );
	}
	
	static char[] WCHARsToStr(wchar[] string){
		return WCHARzToStr(cast(wchar*)string, string.length);
	}
	
	static char[] WCHARzToStr(wchar* pString, int _length = -1)
	{
		// null terminated string pointer
		if(_length == -1){
			_length = 0;
			while(*(pString + _length))
				++_length; 
		}
		if(_length == 0)
			return null;
		// convert wchar* to UTF-8
		wchar[] wcs = pString[0.._length];
		
		return Utf.toString( wcs );
	}

	/** 
	 * <Shawn> difference between WCHARzToStr(pwstr, -1) :
	 * BSTRToStr() internally call WCHARzToStr(pwstr, length) with length set, 
	 * instead to determine the null ended, this mean BSTRToStr() can get string 
	 * which has embedded null characters.
	 */

	version(OLE_COM)
	{ 
	// BSTR is aliased to wchar* 
	// Note : Free the "bstr" memory if freeTheString is true, default false
	static char[] BSTRToStr(/*BSTR*/ inout wchar* bstr, bool freeTheString = false){
		if(bstr is null) return null;
		int size = (SysStringByteLen(bstr) + 1)/wchar.sizeof;
		char[] result = WCHARzToStr(bstr, size);
		if(freeTheString) {
			// free the string and set ptr to null
			SysFreeString(bstr);
			bstr = null;
		}
		return result;
	}
	} // end of OLE_COM
	
	alias Txt.index find;
	alias Txt.substitute replace;
	alias Txt.trim strip;
	
	public static int indexOf(char[] s, char ch){
		return Txt.locate(s, ch);
	}
	
	public static int indexOf(char[] s, char[] ch){
		return Txt.index(s, ch);
	}
	
	public static int indexOf(char[] s, char ch, int start){
		return Txt.locate(s[start..$], ch);
	}
	
	public static int indexOf(char[] s, char[] ch, int start){
		return Txt.index(s[start..$], ch, start);
	}
		
	static static wchar[] _mbcszToWs(char* pMBCS, int len, uint codepage = 0)
	{
		wchar[] wbuf;
		// Convert MBCS to unicode				
		wbuf.length = OS.MultiByteToWideChar(codepage, 0, pMBCS, len, null, 0);
		int n = OS.MultiByteToWideChar(codepage, 0, pMBCS, len, wbuf.ptr, wbuf.length);
		assert(n == wbuf.length);
		return wbuf;
	}	
	
	// static methods
	static int _tcslen(TCHAR* pString){
		int _length = 0;
		while(*(pString + _length))
			++_length; 
		
		return _length;		
	}

	static char[] substring(char[] sc, uint start, uint end)
	{
		if(sc is null || sc.length < end || start > end) return null;
		return sc[start..end];
	}

	static char[] substring(char[] sc, uint start)
	{
		if(sc is null || sc.length < start) return null;
		return sc[start..sc.length];
	}
	static char charAt(char[] sc, int pos)
	{
		if(sc && sc.length > pos) return sc[pos];
		return 0;
	}
	 
	static char[] toHex(uint value, bool prefix = true, int radix = 8){
		return Int.toString(value);
	}

	static char[] tolower(char[] string) {
		TCHAR* ps = StrToTCHARz(string);
		TCHAR* ps2 = OS.CharLower(ps);
		return TCHARzToStr(ps2);
	}
	static char[] toupper(char[] string) {
		TCHAR* ps = StrToTCHARz(string);
		TCHAR* ps2 = OS.CharUpper(ps);
		return TCHARzToStr(ps2);
	}
	
	version(ANSI){
		alias StrToMBCS			StrToTCHARs;
		alias StrToMBCSz		StrToTCHARz;
		alias MBCSzToStr		TCHARzToStr;
		alias MBCSsToStr		TCHARsToStr;
	}else{
		// Unicode
		alias StrToWCHARs		StrToTCHARs;
		alias StrToWCHARz		StrToTCHARz;
		alias WCHARzToStr		TCHARzToStr;
		alias WCHARsToStr		TCHARsToStr;
	}
}	// end of class Convert

alias Converter.StrToTCHARs		StrToTCHARs;
alias Converter.StrToTCHARz		StrToTCHARz;
alias Converter.TCHARzToStr 	TCHARzToStr;
alias Converter.TCHARsToStr 	TCHARsToStr;


